function [Q_need, detail] = compute_svg_need(mpc, CB_steps, pf_lo, pf_hi, P_deadband)
%% 在“固定最佳 CB”的基础上，测算满足考核带宽所需的 SVG 无功 (Mvar)
% - SVG 允许双向（Q 可正可负）
% - 目标 Q 带符号：沿当前 PCC 的无功方向前进（更省 SVG）
% - 二分区间：[-Qbig, +Qbig]，Qbig 取系统量级
%
% 输入：
%   mpc        : MATPOWER 案例（包含 mpc.cb、mpc.svg.gen_idx 等）
%   CB_steps   : 3×1，最佳 CB 台数（整数）
%   pf_lo/hi   : PF 考核带宽（例如 0.90 ~ 0.93）
%   P_deadband : 近零功率死区（|P| <= 此值，不强逼 PF）
%
% 输出：
%   Q_need : 该场景在最佳 CB 下、为达标所需 SVG 给定 (Mvar)，可正可负
%   detail : 结构体，包含中间量

% 参数
%pf_set = (pf_lo + pf_hi)/2;   % 期望点（也可改成 pf_lo = 最小SVG模式）
pf_set = 0.91;
tol_Q  = 1e-3;                % 根精度
maxit  = 20;                  % 二分最大迭代

%% 1) 固定“最佳 CB”（严格：整数台数 × 步长）
m = mpc;
Qstep_Mvar = m.cb(:,2) * m.baseMVA;       % 每步 Mvar
req_steps  = round(CB_steps(:));          % 保险取整
Q_cb_applied = zeros(3,1);

for i = 1:3
    bi    = m.cb(i,1);
    Qd0   = mpc.bus(bi,4);                % 原始负荷无功 (Mvar)
    stepM = Qstep_Mvar(i);

    % 本地不过补保留：这里用 5%（与你阶段1一致即可）
    Qd_floor    = 0.05 * max(Qd0, 0);
    Q_allow     = max(Qd0 - Qd_floor, 0);              % 可抵消上限
    allow_steps = floor(Q_allow/stepM + 1e-9);         % 允许的最大台数
    appl_steps  = min(req_steps(i), allow_steps);      % 实际投切台数（整数）

    Qcb = appl_steps * stepM;           % 实际电容 Mvar
    Q_cb_applied(i) = Qcb;
    m.bus(bi,4) = Qd0 - Qcb;            % 更新 Qd
end

% 初始禁止 SVG 出力（稍后用 eval_f 固定给定值）
if isfield(m,'svg') && isfield(m.svg,'gen_idx') && ~isempty(m.svg.gen_idx)
    gidx = m.svg.gen_idx;
    m.gen(gidx,3) = 0;  m.gen(gidx,4) = 0;  m.gen(gidx,5) = 0;
else
    error('compute_svg_need: 未找到 mpc.svg.gen_idx。');
end
svc_bus = m.svg.bus;

% CB-only 潮流
res_cb = runpf(m);
assert(res_cb.success, 'compute_svg_need: CB-only 潮流不收敛');

% PCC
[Pcb, Qcb_pcc, ~] = slackPQPF(res_cb);
Pabs = abs(Pcb);

%% 2) 目标 Qtarget（带符号）
if Pabs <= P_deadband
    % 近零功率带：不强逼 PF，维持当前 Q
    Qtarget = Qcb_pcc;
else
    % 计算带宽上下界与期望点的 |Q|
    phi_lo   = acos(max(min(pf_lo,1),0));
    phi_hi   = acos(max(min(pf_hi,1),0));
    Q_hi_abs = Pabs * tan(phi_hi);                        % PF 上限 → |Q|较小
    Q_lo_abs = Pabs * tan(phi_lo);                        % PF 下限 → |Q|较大
    Q_set_abs= Pabs * tan(acos(max(min(pf_set,1),0)));    % 期望点 → |Q|

    Qtarget_abs = min(max(Q_set_abs, Q_hi_abs), Q_lo_abs); % 夹到带宽内

    % 沿当前 PCC 的无功方向前进（更省 SVG）；若 Q≈0，则选离当前更近的方向
    sgn = sign(Qcb_pcc);
    if sgn == 0
        cand = [+Qtarget_abs, -Qtarget_abs];
        [~,k] = min(abs(cand - Qcb_pcc));
        Qtarget = cand(k);
    else
        Qtarget = sgn * Qtarget_abs;
    end
end

%% 3) 在 [-Qbig, +Qbig] 上二分（SVG 双向）
Qload_total = sum(mpc.bus(2:4,4));          % 三个负荷点的原始 Qd 之和
Qbig = max(0.5, 2 * Qload_total);         % 系统量级上限

% 先评估端点；若端点不收敛，逐步缩小
[fL, resL] = eval_f(-Qbig);
[fU, resU] = eval_f(+Qbig);
shrink = 0;
while (~isfinite(fL) || ~isfinite(fU)) && shrink < 6
    Qbig = Qbig / 2;
    [fL, resL] = eval_f(-Qbig);
    [fU, resU] = eval_f(+Qbig);
    shrink = shrink + 1;
end
if ~isfinite(fL) && ~isfinite(fU)
    % 实在不收敛：回退到 CB-only
    Q_need  = 0; res_best = res_cb; hit = false;
    warning('compute_svg_need: 端点均不收敛，返回0（请检查电压越限/参数）。');
    detail = pack_detail(); return;
end

% 看 0 点是否已够
[f0, res0] = eval_f(0);
% if isfinite(f0) && abs(f0) <= tol_Q
%     Q_need = 0; res_best = res0; hit = true;
%     detail = pack_detail(); return;
% end

% 区间是否跨零？若没有，则取 |f| 更小的端点（饱和/需增容）
if ~isfinite(fL) || ~isfinite(fU) || (fL * fU > 0)
    if ~isfinite(fL) || (isfinite(fU) && abs(fU) <= abs(fL))
        Q_need = +Qbig; res_best = resU; hit = false;
    else
        Q_need = -Qbig; res_best = resL; hit = false;
    end
else
    % 标准二分
    lo = -Qbig; hi = +Qbig; res_best = res0; fl = fL;
    for it = 1:maxit
        mid = 0.5 * (lo + hi);
        [fm, res_m] = eval_f(mid);
        if ~isfinite(fm)
            % 不收敛：缩小区间
            hi = mid; continue;
        end
        if abs(fm) <= tol_Q || (hi - lo) <= max(tol_Q, 1e-3)
            Q_need = mid; res_best = res_m; hit = true; break;
        end
        % 选择跨零侧
        if fl * fm <= 0
            hi = mid;
        else
            lo = mid; fl = fm;
        end
    end
    if ~exist('Q_need','var'), Q_need = 0.5*(lo+hi); end
end

%% 4) 输出 detail
[P2, Q2, pf2] = slackPQPF(res_best);
detail = struct('Q_cb',Q_cb_applied, 'Qtarget',Qtarget, 'Qbig',Qbig, ...
                'Pcb',Pcb,'Qcb_pcc',Qcb_pcc,'P2',P2,'Q2',Q2,'pf2',pf2, ...
                'hit',logical(hit));
fprintf('[need] Pcb=%.3f Qcb=%.3f => Qtarget=%.3f  Q_need=%.3f  hit=%d  Qbig=%.3f\n', ...
        Pcb, Qcb_pcc, Qtarget, Q_need, detail.hit, detail.Qbig);

%% ===== 嵌套：固定 SVG=Qsvc 后评估 f(Qsvc) =====
    function [fval, res] = eval_f(Qsvc)
        mm = m;                       % 基于“已投好 CB”的网络
        % 固定 SVG 给定：把该 gen 的 Qmax=Qmin=Qsvc
        mm.gen(gidx,3) = Qsvc;        % Qg
        mm.gen(gidx,4) = Qsvc;        % Qmax
        mm.gen(gidx,5) = Qsvc;        % Qmin
        % 占位的电压参考
        Vm_here = res_cb.bus(res_cb.bus(:,1)==svc_bus, 8);
        if isempty(Vm_here), Vm_here = 1.00; end
        mm.gen(gidx,6) = Vm_here;

        res = runpf(mm);
        if ~res.success
            fval = NaN;               % 用 NaN 表示不可行，外层会缩区
            return;
        end
        [~, Qslack] = slackPQPF(res);
        fval = Qslack - Qtarget;      % 目标方程：Q_slack(Qsvc) - Qtarget = 0
    end

%% ===== 打包 detail 的小函数 =====
    function d = pack_detail()
        d = struct('Q_cb',Q_cb_applied,'Qtarget',Qtarget,'Qbig',Qbig,'hit',false);
    end
end

%% ====== 工具函数 ======
function [P_slack, Q_slack, pf_val] = slackPQPF(res)
slack_idx = (res.gen(:,1) == 1);      % 平衡母线 = 1
P_slack = sum(res.gen(slack_idx, 2));
Q_slack = sum(res.gen(slack_idx, 3));
S = hypot(P_slack, Q_slack);
if S < 1e-9, pf_val = 1.0; else, pf_val = P_slack / S; end
end
